/*
  Copyright (c) 2010
  The Regents of the University of Michigan
  All Rights Reserved

  Permission is granted to use, copy, create derivative works, and
  redistribute this software and such derivative works for any purpose,
  so long as the name of the University of Michigan is not used in
  any advertising or publicity pertaining to the use or distribution
  of this software without specific, written prior authorization. If
  the above copyright notice or any other identification of the
  University of Michigan is included in any copy of any portion of
  this software, then the disclaimer below must also be included.

  This software is provided as is, without representation or warranty
  of any kind either express or implied, including without limitation
  the implied warranties of merchantability, fitness for a particular
  purpose, or noninfringement.  The Regents of the University of
  Michigan shall not be liable for any damages, including special,
  indirect, incidental, or consequential damages, with respect to any
  claim arising out of or in connection with the use of the software,
  even if it has been or is hereafter advised of the possibility of
  such damages.
*/
/*
 *  mojito_serialization.c
 *  DHT
 *
 *  Created by Dhritiman Sagar.
 *
 */

#include "mojito_serialization.h"
#include "mojito_types.h"
#include "mojito_constants.h"


#include <glib.h>
#include <string.h>
#include <stdlib.h>

#include <sys/types.h>
#include <arpa/inet.h> 

#define MOJITO_HEADER_LEN 61
#define CONTACT_SIZE 33
#define BASE_DHT_VALUE_LEN 61
#define MOJITO_SOCKET_ADDR_LEN 7
#define BASE_SECURITY_TOKEN_LEN 1
#define BASE_STATUS_CODE_LEN 4
#define BIG_INTEGER_LEN (1 + sizeof(uint32_t))

#define RETURN_IF_NEGATIVE(x) do { \
int ret = (x);                   \
if (ret < 0) {                   \
return ret;                    \
}                                \
} while(0)


/* extract a value of the given type into the lvalue target. Requires that
 packet is a char buf holding the value to extract in network byte order. 
 Macro taken directly from serialization.h */
#define DECODE_PRIMITIVE_TYPE(target)  do{       \
typedef typeof(target) type;                 \
type *ptr = (type *) packet;                 \
packet += sizeof(type);                      \
if (G_UNLIKELY(packet > end)) {              \
goto ERROR;                                \
}                                            \
(target) = *ptr;                             \
if (sizeof(target) == 8) {                   \
(target) = bswap_64(target);                \
}                                            \
else if (sizeof(target) == 4) {              \
(target) = ntohl(target);                  \
}                                            \
else if (sizeof(target) == 2) {              \
(target) = ntohs(target);                  \
}                                            \
else if (sizeof(target) != 1) {              \
assert(0);                                 \
}                                            \
} while(0)



static inline int decode_mojito_contact(const char* packet, const char* end, 
										struct Contact* contact)
{
	DECODE_PRIMITIVE_TYPE((contact->vendor));                                       
											                                   
	DECODE_PRIMITIVE_TYPE((contact->version));                                      
														                       
	memcpy(&(contact->kuid.bytes), packet, KUID_SIZE);                              
	packet += KUID_SIZE;                                                           
                                                                               
	DECODE_PRIMITIVE_TYPE((contact->sock_addr.len));                                
														                       
	DECODE_PRIMITIVE_TYPE((contact->sock_addr.addr.s_addr));                        
	contact->sock_addr.addr.s_addr = htonl(contact->sock_addr.addr.s_addr);     
																			   
	DECODE_PRIMITIVE_TYPE((contact->sock_addr.port));                               
	contact->sock_addr.port = htons((contact->sock_addr.port));
	return CONTACT_SIZE;
ERROR:
	return -1;
}


static inline int decode_mojito_dht_value(const char* packet, const char* end,
										  struct DHTValue* dhtv)
{                                      
	packet += decode_mojito_contact(packet, end, &dhtv->contact);                                           
	memcpy(dhtv->kuid.bytes, packet, KUID_SIZE);                                    
	packet += KUID_SIZE;                                                           
	DECODE_PRIMITIVE_TYPE(dhtv->value_type);                                        
	DECODE_PRIMITIVE_TYPE(dhtv->version);                                           
	DECODE_PRIMITIVE_TYPE(dhtv->len);                                               
	dhtv->value_bytes = g_malloc0(dhtv->len);                                       
	memcpy(dhtv->value_bytes, packet, dhtv->len);                                    
	packet += dhtv->len;
	
	return (BASE_DHT_VALUE_LEN + dhtv->len);
ERROR:
	return -1;
}



static inline int decode_mojito_header(const char* packet, const char* end,
									   struct MojitoHeader* out)
{
	g_assert(out);
	memcpy(&(out->message_id), packet, MESSAGE_ID_SIZE);
	packet += MESSAGE_ID_SIZE;
	DECODE_PRIMITIVE_TYPE(out->f_dht_message);
	g_assert(packet - end > 0);
	
	DECODE_PRIMITIVE_TYPE(out->version);
	memcpy(&(out->length), packet, sizeof(uint32_t)); /*Stored as little endian*/
	packet += sizeof(uint32_t);
	
	DECODE_PRIMITIVE_TYPE(out->opcode);
	
	packet += decode_mojito_contact(packet, end, &(out->contact));
	 
	
	DECODE_PRIMITIVE_TYPE(out->contact_instance_id);
	
	DECODE_PRIMITIVE_TYPE(out->contact_flags);
	
	DECODE_PRIMITIVE_TYPE(out->extended_header_len);
	
	return 0;
											   
ERROR:
	return -1;
	
}


static inline int decode_mojito_ping_request(const struct MojitoHeader *header,
											 struct MojitoPingRequestPacket* out,
											 const char *packet, const char *end,
											 struct Contact *contact)
{

	/*Nothing to decode for ping request*/
	return 0;
}


int decode_mojito_find_node_request(const struct MojitoHeader *header,
									struct MojitoFindNodeRequestPacket* out,
									const char *packet, const char *end,
									struct Contact *contact)
{
	g_assert(packet < end);
	g_assert(out);
	
	memcpy(&(out->target_kuid.bytes), packet, KUID_SIZE);  
ERROR:
	return -1;
}


int decode_mojito_store_request(const struct MojitoHeader *header,
								struct MojitoStoreRequestPacket* out,
								const char *packet, const char *end,
								struct Contact *contact)
{
	int i;
	g_assert(packet < end);
	g_assert(out);
	
	DECODE_PRIMITIVE_TYPE((out->st.len));
	
	out->st.sec_token = g_malloc0(out->st.len);
	
	memcpy(out->st.sec_token, packet, out->st.len);
	
	packet += out->st.len;
	
	DECODE_PRIMITIVE_TYPE(out->count);
	out->values = g_malloc0(out->count * sizeof(struct DHTValue));
	for(i = 0; i < out->count; ++i)
	{
		g_assert(packet < end);
		packet += decode_mojito_dht_value(packet, end, &(out->values[i]));
	}
	
ERROR:
	return -1;
	
}


int decode_mojito_find_value_request(const struct MojitoHeader *header,
									 struct MojitoFindValueRequestPacket* out,
									 const char *packet, const char *end,
									 struct Contact *contact)
{
	int i;
	g_assert(packet < end);
	memcpy(out->primary_key.bytes, packet, KUID_SIZE);
	packet += KUID_SIZE;
	g_assert(packet < end);
	
	DECODE_PRIMITIVE_TYPE(out->count);
	
	out->secondary_keys = g_malloc0(out->count * sizeof(struct kuid_t));
	
	for(i = 0; i < out->count; ++i)
	{
		g_assert(packet < end);
		memcpy(out->secondary_keys[i].bytes, packet, KUID_SIZE);
		packet += KUID_SIZE;
		
	}
	
	return 0;
ERROR:
	return -1;
}

static inline int decode_mojito_ping_reply(const struct MojitoHeader *header,
										   struct MojitoPingReplyPacket* out,
										   const char *packet, const char *end,
										   struct Contact *contact)
{
	g_assert(out);
	DECODE_PRIMITIVE_TYPE(out->requester_address.len);
	DECODE_PRIMITIVE_TYPE(out->requester_address.addr.s_addr);
	out->requester_address.addr.s_addr = htonl(out->requester_address.addr.s_addr);
	DECODE_PRIMITIVE_TYPE(out->requester_address.port);
	out->requester_address.port = htons(out->requester_address.port);
	DECODE_PRIMITIVE_TYPE(out->estimated_dht_size.len);
	DECODE_PRIMITIVE_TYPE(out->estimated_dht_size.val);
	
	return 0;
	
ERROR:
	return -1;
}

int decode_mojito_find_node_reply(const struct MojitoHeader *header,
								  struct MojitoFindNodeReplyPacket* out,
								  const char *packet, const char *end,
								  struct Contact *contact)
{
	int i;
	g_assert(out);
	g_assert(packet < end);
	
	DECODE_PRIMITIVE_TYPE(out->st.len);
	
	out->st.sec_token = g_malloc0(out->st.len);
	memcpy(out->st.sec_token, packet, out->st.len);
	packet += out->st.len;
	
	DECODE_PRIMITIVE_TYPE(out->count);
	
	out->contacts = g_malloc0(out->count * sizeof(struct Contact));
	for(i = 0; i < out->count; ++i)
	{
		g_assert(packet < end);
		packet += decode_mojito_contact(packet, end, &out->contacts[i]);
	}

	return 0;
	
ERROR:
	return -1;
	
}



int decode_mojito_store_reply(const struct MojitoHeader *header,
							  struct MojitoStoreReplyPacket* out,
							  const char *packet, const char *end,
							  struct Contact *contact)
{
	int i;
	g_assert(out);
	g_assert(packet < end);
	
	DECODE_PRIMITIVE_TYPE(out->count);
	out->sc = g_malloc0(out->count * sizeof(struct StatusCode));
	
	for(i = 0; i < out->count; ++i)
	{
		g_assert(packet < end);
		DECODE_PRIMITIVE_TYPE(out->sc[i].status_code);
		DECODE_PRIMITIVE_TYPE(out->sc[i].len);
		packet += out->sc[i].len; /*Don't need the description*/
		out->sc[i].descript = NULL;
	}
	
	return 0;
	
ERROR:
	return -1;
}

int decode_mojito_find_value_reply(const struct MojitoHeader *header,
								   struct MojitoFindValueReplyPacket* out,
								   const char *packet, const char *end,
								   struct Contact *contact)
{
	int i;
	g_assert(out);
	g_assert(packet < end);
	
	memcpy(&out->request_load, packet, sizeof(float));
	packet += sizeof(float);
	
	DECODE_PRIMITIVE_TYPE(out->value_count);
	
	out->values = g_malloc0(out->value_count * sizeof(struct DHTValue));
	
	for(i = 0; i < out->value_count; ++i)
	{
		g_assert(packet < end);
		packet += decode_mojito_dht_value(packet, end, &out->values[i]);
	}
	
	DECODE_PRIMITIVE_TYPE(out->secondary_key_count);
	out->secondary_keys = g_malloc0(out->secondary_key_count * sizeof(struct kuid_t));
	for(i = 0; i < out->secondary_key_count; ++i)
	{
		g_assert(packet < end);
		memcpy(&out->secondary_keys[i], packet, KUID_SIZE);
		packet += KUID_SIZE;
		
	}
	
	
	return 0;
ERROR:
	return -1;
}



static inline void encode_mojito_socket_address(const struct SocketAddress* sock_addr, char** buff)
{
	memcpy(*buff, &sock_addr->len, sizeof(uint8_t));
	*buff += sizeof(uint8_t);
	
	memcpy(*buff, &sock_addr->addr.s_addr, sizeof(uint32_t));
	*buff += sizeof(uint32_t);
	
	memcpy(*buff, &sock_addr->port, sizeof(uint16_t));
	*buff += sizeof(uint16_t);
	
}


static inline void encode_mojito_contact(const struct Contact* contact, char** buff)
{
	memcpy(*buff, &contact->vendor, sizeof(vendor_type_t));
	*buff += sizeof(vendor_type_t);
	version_t version = htons(contact->version);
	memcpy(*buff, &version, sizeof(version_t));
	*buff += sizeof(version_t);
	memcpy(*buff,contact->kuid.bytes, KUID_SIZE);
	*buff += KUID_SIZE;
	
	encode_mojito_socket_address(&contact->sock_addr, buff);
	
}

static inline int encode_mojito_header(const struct MojitoHeader* header, char** buff)
{
	memcpy(*buff, header->message_id, MESSAGE_ID_SIZE);
	*buff += MESSAGE_ID_SIZE;
	
	
	memcpy(*buff, &header->f_dht_message, sizeof(uint8_t));
	*buff += sizeof(uint8_t);
		   
	version_t version = htons(header->version);
	memcpy(*buff, &version, sizeof(version_t));
	*buff += sizeof(version_t);
		   
	
	uint32_t len = htonl(header->length);
	memcpy(*buff, &len, sizeof(uint32_t));
	*buff += sizeof(uint32_t);
		   
	uint8_t opcode = (uint8_t)header->opcode;
	memcpy(*buff, &(opcode), sizeof(uint8_t));
	*buff += sizeof(uint8_t);
		   
	
	encode_mojito_contact(&header->contact, buff);
		   
	
	memcpy(*buff, &(header->contact_instance_id), sizeof(uint8_t));
	*buff += sizeof(uint8_t);
	
	memcpy(*buff, &(header->contact_flags), sizeof(uint8_t));
	*buff += sizeof(uint8_t);
	
	
	uint16_t ex_len = htons(header->extended_header_len);
	memcpy(*buff, &(ex_len), sizeof(uint16_t));
	*buff += sizeof(uint16_t);
		   
}



static inline int encode_mojito_dht_value(const struct DHTValue* dhtv, char** buff)
{
	encode_mojito_contact(&dhtv->contact, buff);
	
	memcpy(*buff, dhtv->kuid.bytes, KUID_SIZE);
	*buff += KUID_SIZE;
	
	dht_value_type_t value = htonl(dhtv->value_type);
	memcpy(*buff, &value, sizeof(dht_value_type_t));
	*buff += sizeof(dht_value_type_t);
	
	version_t version = htons(dhtv->version);
	memcpy(*buff, &version, sizeof(version_t));
	*buff += sizeof(version_t);
	
	uint16_t len = htons(dhtv->len);
	memcpy(*buff, &len, sizeof(uint16_t));
	*buff += sizeof(uint16_t);
	
	memcpy(*buff, dhtv->value_bytes, dhtv->len);
	*buff += sizeof(dhtv->len);
}


									   
							
/*User is responsible for freeing the buffers allocated using g_malloc*/
									   
void encode_mojito_ping_request(const struct MojitoPingRequestPacket *packet, char **usr_buff,
								size_t *buflen)
{
	char* buff = g_malloc0(MOJITO_HEADER_LEN);
	*buflen = MOJITO_HEADER_LEN;
	char* base_addr = buff;
	encode_mojito_header(&packet->header, &buff);
	*usr_buff = base_addr;
}


void encode_mojito_find_node_request(const struct MojitoFindNodeRequestPacket *packet,
									 char **usr_buff, size_t *buflen)
{
	char* buff = g_malloc0(MOJITO_HEADER_LEN + KUID_SIZE);
	*buflen = MOJITO_HEADER_LEN + KUID_SIZE;
	char* base_addr = buff;
	encode_mojito_header(&packet->header, &buff);
	memcpy(buff, &packet->target_kuid, KUID_SIZE);
	
	*usr_buff = base_addr;
}

/*Currently only support encoding requests with single value*/
void encode_mojito_store_request(const struct MojitoStoreRequestPacket *packet,
								 char **usr_buff, size_t *buflen)
{
	g_assert(packet->count == 1);
	char* buff = g_malloc0(MOJITO_HEADER_LEN + sizeof(uint8_t) +
							   packet->st.len + sizeof(uint8_t) +
							   packet->count *(BASE_DHT_VALUE_LEN + 
											   packet->values[0].len));
	*buflen = MOJITO_HEADER_LEN + sizeof(uint8_t) +
			  packet->st.len + sizeof(uint8_t) +
			  packet->count *(BASE_DHT_VALUE_LEN + 
					packet->values[0].len);
	char* base_addr = buff;
	
	encode_mojito_header(&packet->header, &buff);
	
	memcpy(buff, &packet->st.len, sizeof(uint8_t));
	buff += sizeof(uint8_t);
	
	memcpy(buff, packet->st.sec_token, packet->st.len);
	buff += packet->st.len;
	
	memcpy(buff, &packet->count, sizeof(uint8_t));
	buff += sizeof(uint8_t);
	
	encode_mojito_dht_value(&packet->values[0], &buff);
	
	
	*usr_buff = base_addr; 
				
}


/*Currently only supports finding single value*/
void encode_mojito_find_value_request(const struct MojitoFindValueRequestPacket *packet,
									  char **usr_buff, size_t *buflen)
{
	g_assert(packet->count == 0);
	char* buff = g_malloc0(MOJITO_HEADER_LEN + KUID_SIZE +
							   sizeof(uint8_t));
	char* base_addr = buff;
	
	encode_mojito_header(&packet->header, &buff);
	
	memcpy(buff, packet->primary_key.bytes, KUID_SIZE);
	buff += KUID_SIZE;
	
	memcpy(buff, &packet->count, sizeof(uint8_t));
	buff += sizeof(uint8_t);
	
	*usr_buff = base_addr;
	
	
}


void encode_mojito_ping_reply(const struct MojitoPingReplyPacket *packet, char **usr_buf,
							  size_t *buflen)
{
	*buflen = MOJITO_HEADER_LEN + MOJITO_SOCKET_ADDR_LEN + BIG_INTEGER_LEN;
	char* buff = g_malloc0(*buflen);
	char* base_addr = buff;
	
	encode_mojito_header(&packet->header, &buff);
	
	encode_mojito_socket_address(&packet->requester_address, &buff);
	
	memcpy(buff, &packet->estimated_dht_size.len, sizeof(uint8_t));
	buff += sizeof(uint8_t);
	
	uint32_t val = htonl(packet->estimated_dht_size.val);
	memcpy(buff, &val, sizeof(uint32_t));
	buff += sizeof(unsigned long long int);
	
	*usr_buf = base_addr;
}


void encode_mojito_find_node_reply(const struct MojitoFindNodeReplyPacket *packet,
								   char **usr_buf, size_t *buflen)
{
	int i;
	*buflen = (MOJITO_HEADER_LEN + BASE_SECURITY_TOKEN_LEN + packet->st.len +
			   sizeof(uint8_t) + packet->count * (CONTACT_SIZE));
	char* buff = g_malloc0(*buflen);
	char* base_addr = buff;
	
	encode_mojito_header(&packet->header, &buff);
	
	memcpy(buff, &packet->st.len, BASE_SECURITY_TOKEN_LEN);
	buff += BASE_SECURITY_TOKEN_LEN;
	
	memcpy(buff, packet->st.sec_token, packet->st.len);
	buff += packet->st.len;
	
	memcpy(buff, &packet->count, sizeof(uint8_t));
	buff += sizeof(uint8_t);
	
	for(i = 0; i < packet->count; ++i)
	{
		encode_mojito_contact(&packet->contacts[i], &buff);
	}
	
	*usr_buf = base_addr;
}



/*Only send back a single status code without description*/
void encode_mojito_store_reply(const struct MojitoStoreReplyPacket *packet,
							   char **usr_buf, size_t *buflen)
{
	int i;
	g_assert(packet->count == 1);
	*buflen = (MOJITO_HEADER_LEN + sizeof(uint8_t) + 
			   packet->count * BASE_STATUS_CODE_LEN);
	char* buff = g_malloc0(*buflen);
	char* base_addr = buff;
	
	encode_mojito_header(&packet->header, &buff);
	
	memcpy(buff, &packet->count, sizeof(uint8_t));
	buff += sizeof(uint8_t);
	
	uint16_t code = htons(packet->sc[0].status_code);
	memcpy(buff, &code, sizeof(uint16_t));
	buff += sizeof(uint16_t);
	
	uint16_t len = 0;
	memcpy(buff, &len, sizeof(uint16_t));
	buff += sizeof(uint16_t);
	
	/*We do not encode the description*/
	*usr_buf = base_addr;
}


/*We do not send back any values or secondary keys*/
void encode_mojito_find_value_reply(const struct MojitoFindValueReplyPacket *packet,
									char **usr_buf, size_t *buflen)
{
	*buflen = MOJITO_HEADER_LEN + sizeof(float) + 2*sizeof(uint8_t);
	char* buff = g_malloc0(*buflen);
	char* base_addr = buff;
	
	encode_mojito_header(&packet->header, &buff);
	
	memcpy(buff, &packet->request_load, sizeof(float));
	buff += sizeof(float);
	
	uint8_t value_count = 0;
	memcpy(buff, &value_count, 1);
	buff += 1;
	
	uint8_t secondary_key_count = 0;
	memcpy(buff, &secondary_key_count, 1);
	buff += 1;
	
	*usr_buf = base_addr;
	
}




















